package com.idroc.cdfs.node.impl;

import java.io.File;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ConcurrentMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.idroc.cdfs.node.ICursor;
import com.idroc.cdfs.node.INode;
import com.sleepycat.bind.EntryBinding;
import com.sleepycat.bind.serial.ClassCatalog;
import com.sleepycat.bind.serial.SerialBinding;
import com.sleepycat.bind.serial.StoredClassCatalog;
import com.sleepycat.collections.StoredMap;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.CursorConfig;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;

/**
 * 存储节点
 * @author zhandl(yuyoo4j@163.com)
 * @companay www.idroc.com
 * @2009-8-8 下午11:56:20
 */
public final class StorageNode implements INode {
	
	private static final Logger log = LoggerFactory.getLogger(StorageNode.class);
	
	public static final String NODE_STORE_HOME = "node_store_home";
	
	public static final String NODE_DATABASE = "cdfs_node_database";
	 		
	private Properties props = null;
	
	private EnvironmentConfig envConfig = null;
	private Environment nodeEnv = null;
	private DatabaseConfig dbConfig = null;
	private Database nodeDb = null;
 	
	public boolean delete(byte[] key) {
		
		DatabaseEntry k = new DatabaseEntry(key);
		try {
			OperationStatus os = nodeDb.delete(null, k);
			return os == OperationStatus.SUCCESS;
		} catch (DatabaseException dbe) {
			log.error("节点删除数据异常, key:" + new String(key));
			return false;
		}
	}

	public byte[] find(final byte[] key) {
		
		DatabaseEntry k = new DatabaseEntry(key);
		DatabaseEntry v = new DatabaseEntry();
		try {
			OperationStatus os = nodeDb.get(null, k, v, LockMode.DEFAULT);
			if (os == OperationStatus.SUCCESS) {
				return v.getData();
			} else {
				return null;
			}
		} catch (DatabaseException dbe) {
			log.error("节点查找数据异常, key:" + new String(key));
			return null;
		}
	} 

	public boolean store(final byte[] key, final byte[] value) {
		
		DatabaseEntry k = new DatabaseEntry(key);
		DatabaseEntry v = new DatabaseEntry(value);
		try {
			OperationStatus os = nodeDb.put(null, k, v);
			if (os == OperationStatus.SUCCESS) {
				nodeDb.sync();
				return true;
			} 
		} catch (DatabaseException dbe) {
			log.error("节点保存数据异常, key:" + new String(key)); 
		}
		return false;
	}

	public void initialize(final Properties props) {

		if (null == props) {
			throw new IllegalArgumentException("非法初始化参数. props不能为null.");
		}
		this.props = props;
		init();
	}
	
	private void init() {
		try {
			String dbEnvPath = (String) props.get(NODE_STORE_HOME);
		    envConfig = new EnvironmentConfig();
		    envConfig.setAllowCreate(true);
		    envConfig.setSharedCache(true);
		    nodeEnv = new Environment(new File(dbEnvPath.trim()), envConfig);		    
		} catch (DatabaseException dbe) { 
			log.error("节点初始化异常.", dbe);
		} 
	}

	/**
	 * 关闭存储节点
	 */
	public boolean close() {
		
		boolean closed = true;
		if(null != nodeDb) {
			try { 
				nodeDb.close();
			} catch (DatabaseException dbe) { 
				closed = false;
				log.error("关闭Berkeley数据库(Database)异常", dbe);
			}
		}
		if(null != nodeEnv) {
			try { 
				nodeEnv.close();
			} catch (DatabaseException dbe) { 
				closed = false;
				log.error("关闭Berkeley数据库环境(Environment)异常", dbe);
			}
		}
		if (!closed) {			
			log.error("关闭存储节点异常"); 
		}
		return closed;
	}

	public boolean open() {
		
	    try {
			dbConfig = new DatabaseConfig();
		    dbConfig.setAllowCreate(true); 
		    dbConfig.setDeferredWrite(true);
			nodeDb = nodeEnv.openDatabase(null, NODE_DATABASE, dbConfig);
			return true;
		} catch (DatabaseException ex) {
			log.error("打开节点异常", ex);
			return false;
		} 
	}

	public ICursor openCursor() {
		
		SNCursor c = new SNCursor(nodeDb);
		return c;
	}
		
	/**
	 * 内部数据项游标类
	 * @author zhandl(yuyoo4j@163.com)
	 * @companay www.idroc.com
	 * @2009-8-9 上午04:09:50
	 */
	private class SNCursor implements ICursor {
		
		private final Logger log = LoggerFactory.getLogger(this.getClass());
		
		private Database db = null;
		private Cursor bdbCursor = null;
		private CursorConfig cc = null;
		private DatabaseEntry currKey = null;
		private DatabaseEntry currVal = null;
		
		SNCursor(Database db) {
			this.db = db;
			this.cc = new CursorConfig();
			cc.setReadCommitted(true);
			try {
				this.bdbCursor = db.openCursor(null, cc);
			} catch (DatabaseException e) {
				String errorMsg = "打开数据库游标异常";
				log.error(errorMsg, e);
				throw new RuntimeException(errorMsg, e);
			}
		}

		public void close() {
			
			try {
				bdbCursor.close();
			} catch (DatabaseException e) {
				log.error("关闭异常", e);
			}
		}

		public long count() {
			try {
				return (long)bdbCursor.count();
			} catch (DatabaseException dbe) {
				log.error("计算数据项数量异常", dbe);
				throw new RuntimeException("无法计算数量", dbe);
			}
		}

		public boolean delete() {
			try {
				return db.delete(null, currKey) == OperationStatus.SUCCESS;
			} catch (DatabaseException dbe) {
				log.error("删除当前数据项异常", dbe);
				throw new RuntimeException("无法删除当前数据项", dbe);
			}
		}

		public boolean find(byte[] key) {
			
			try {
				DatabaseEntry k = new DatabaseEntry(key);
				DatabaseEntry v = new DatabaseEntry();
				OperationStatus os = db.get(null, k, v, LockMode.DEFAULT);
				log.debug("find key:{}, val:{}", k, v);
				if (os == OperationStatus.SUCCESS) {
					currKey = k;
					currVal = v;
					return true;
				} else {
					return false;
				}
			} catch (DatabaseException dbe) {
				log.error("查找数据项异常", dbe);
				throw new RuntimeException("无法查找数据项", dbe);
			}
		}

		public boolean getFirst() { 
			
			try {
				DatabaseEntry k = new DatabaseEntry();
				DatabaseEntry v = new DatabaseEntry();
				OperationStatus os = bdbCursor.getFirst(k, v, LockMode.DEFAULT);
				log.debug("get first key:{}, val:{}", k, v);
				if (os == OperationStatus.SUCCESS) {
					currKey = k;
					currVal = v; 
					return true;
				} else { 
					return false;
				}
			} catch (DatabaseException dbe) {
				log.error("定位第一个数据项异常", dbe);
				throw new RuntimeException("无法查找第一个数据项", dbe);
			}
		}

		public byte[] getKey() {
			return currKey.getData();
		}

		public boolean getLast() {
			
			try {
				DatabaseEntry k = new DatabaseEntry();
				DatabaseEntry v = new DatabaseEntry();
				OperationStatus os = bdbCursor.getLast(k, v, LockMode.DEFAULT);
				log.debug("get last key:{}, val:{}", k, v);
				if (os == OperationStatus.SUCCESS) {
					currKey = k;
					currVal = v;
					return true;
				} else {
					return false;
				}
			} catch (DatabaseException dbe) {
				log.error("定位最后一个数据项异常", dbe);
				throw new RuntimeException("无法查找最后一个数据项", dbe);
			}
		}

		public boolean getNext() {
			
			try {
				DatabaseEntry k = new DatabaseEntry();
				DatabaseEntry v = new DatabaseEntry();
				OperationStatus os = bdbCursor.getNext(k, v, LockMode.DEFAULT);
				log.debug("get next key:{}, val:{}", k, v);
				if (os == OperationStatus.SUCCESS) {
					currKey = k;
					currVal = v;
					return true;
				} else {
					return false;
				}
			} catch (DatabaseException dbe) {
				log.error("查找下一个数据项异常", dbe);
				throw new RuntimeException("无法查找下一个数据项", dbe);
			}
		}

		public boolean getPrev() {
			
			try {
				DatabaseEntry k = new DatabaseEntry();
				DatabaseEntry v = new DatabaseEntry();
				OperationStatus os = bdbCursor.getPrev(k, v, LockMode.DEFAULT);
				log.debug("get prev key:{}, val:{}", k, v);
				if (os == OperationStatus.SUCCESS) {
					currKey = k;
					currVal = v;
					return true;
				} else {
					return false;
				}
			} catch (DatabaseException dbe) {
				log.error("查找上一个数据项异常", dbe);
				throw new RuntimeException("无法查找上一个数据项", dbe);
			}
		}

		public byte[] getValue() {
			return currVal.getData();
		}

		public boolean store(byte[] value) {
			try {
				DatabaseEntry k = new DatabaseEntry();
				DatabaseEntry v = new DatabaseEntry();
				OperationStatus os = db.put(null, k, v);
				log.debug("store key:{}, val:{}", k, v);
				if (os == OperationStatus.SUCCESS) {
					currKey = k;
					currVal = v;
					return true;
				} else {
					return false;
				}
			} catch (DatabaseException dbe) {
				log.error("保存数据项异常", dbe);
				throw new RuntimeException("无法保存数据项", dbe);
			}
		}
		
	}
	
	public BerkeleyCollection getBerkeleyCollection(String classCatalog, Class keyClazz, Class valueClazz) {
		
		try {
			Database catalogDb = nodeEnv.openDatabase(null, classCatalog, dbConfig);
			StoredClassCatalog cc = new StoredClassCatalog(catalogDb);
			BerkeleyCollection coll = new BerkeleyCollection(catalogDb, classCatalog, cc, keyClazz, valueClazz);
			return coll;
		} catch (DatabaseException dbe) {
			String errorMsg = "获取Berkeley数据项容器(BerkeleyCollection)对象异常.";
			log.error(errorMsg, dbe);
			throw new RuntimeException(errorMsg, dbe);
		}
	}

	/**
	 * @author zhandl(yuyoo4j@163.com)
	 * @companay www.idroc.com
	 * @2009-8-9 上午04:17:58
	 */
	public class BerkeleyCollection {

		private final Logger log = LoggerFactory.getLogger(this.getClass());
		 
		private Database db = null;
		private String catalog = null;
		private StoredClassCatalog classCatalog = null; 
		private Class kc = null;
		private Class vc = null;
		
		private BerkeleyCollection(Database db, String catalog, StoredClassCatalog classCatalog, Class keyClazz, Class valueClazz) {
			
			this.db = db;
			this.catalog = catalog;
			this.classCatalog = classCatalog;
			this.kc = keyClazz;
			this.vc = valueClazz;
		}
		
		/**
		 * 打开StoredMap
		 * @return StoredMap
		 */
		public StoredMap open() {
			
			EntryBinding keyEB = new SerialBinding(classCatalog, kc);
			EntryBinding valEB = new SerialBinding(classCatalog, vc);
			StoredMap sm = new StoredMap(db, keyEB, valEB, true);
			return sm;
		}
		
		/**
		 * 关闭资源
		 *
		 */
		public void close() {
			
			if (null != classCatalog) {
				try { classCatalog.close(); } catch (Exception ex) { /* swallow Exception */ }
			}
			
			if (null != db) {
				try { db.close(); } catch (Exception ex) { /* swallow Exception */ }
			}
		}
	}
	 
	@Override
	public String toString() {
		
		StringBuilder sb = new StringBuilder(); 
		sb.append("props:").append(props);
		return sb.toString();
	}
}
